<<<<<<< HEAD
﻿# 调用 ResNet-34 模型

import torch
from torch import nn
import torchvision.models as models
from torchsummary import summary

import torch
import torch.nn as nn
import torch.nn.functional as F

class ResidualBlock(nn.Module):
    """
    实现 ResNet 中的残差块 (Residual Block)。
    每个残差块包含两个卷积层和一个跳跃连接 (Shortcut)。
    """
    def __init__(self, in_channels, out_channels, stride=1):
        """
        初始化残差块的卷积层和跳跃连接。
        
        参数：
        - in_channels: 输入特征图的通道数。
        - out_channels: 输出特征图的通道数。
        - stride: 当前卷积层的步幅，用于控制特征图尺寸变化。
        """
        super(ResidualBlock, self).__init__()
        
        # 1主分支的第一个卷积层
        self.conv1 = nn.Conv2d(
            in_channels,                # 输入通道数
            out_channels,               # 输出通道数
            kernel_size=3,              # 卷积核大小为 3x3
            stride=stride,              # 步幅控制特征图大小变化
            padding=1,                  # 保持特征图大小不变（3x3卷积时，padding=1）
            bias=False                  # 不使用偏置，因为后续有批归一化
        )
        self.bn1 = nn.BatchNorm2d(out_channels)  # 第一个卷积层后的批归一化层
        
        # 2主分支的第二个卷积层
        self.conv2 = nn.Conv2d(
            out_channels,               # 输入通道数等于上层的输出通道数
            out_channels,               # 输出通道数保持不变
            kernel_size=3,              # 卷积核大小为 3x3
            stride=1,                   # 步幅固定为 1，不改变特征图大小
            padding=1,                  # 保持特征图大小不变
            bias=False                  # 不使用偏置
        )
        self.bn2 = nn.BatchNorm2d(out_channels)  # 第二个卷积层后的批归一化层
        
        # 3初始化跳跃连接 (shortcut)
        self.shortcut = nn.Sequential()  # 默认初始化为恒等映射 (identity mapping)
        
        # 4如果 stride != 1 或 输入通道数与输出通道数不一致，需要调整输入
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(
                    in_channels,            # 输入通道数
                    out_channels,           # 输出通道数
                    kernel_size=1,          # 使用 1x1 卷积调整通道数
                    stride=stride,          # 使用当前块的步幅
                    bias=False              # 不使用偏置
                ),
                nn.BatchNorm2d(out_channels)  # 卷积后的批归一化
            )


    def forward(self, x):
        """
        定义前向传播逻辑。
        
        参数：
        - x: 输入的特征图张量，形状为 (batch_size, in_channels, H, W)。
        
        返回：
        - out: 输出的特征图张量，形状为 (batch_size, out_channels, H', W')。
        """
        # 主分支 (通过两个卷积层处理输入)
        out = self.conv1(x)           # 第一个卷积
        out = self.bn1(out)           # 批归一化
        out = F.relu(out)             # 激活函数 ReLU
        
        out = self.conv2(out)         # 第二个卷积
        out = self.bn2(out)           # 批归一化
        
        # 将主分支的输出与跳跃连接的输出相加
        out += self.shortcut(x)       # 跳跃连接 (可能是调整后的输入)
        out = F.relu(out)             # 激活函数 ReLU
        return out


class ResNet18(nn.Module):
    def __init__(self,num_classes=1000,pretrained=False):
        super(ResNet18, self).__init__()
        '''
        if pretrained:
            # 加载 PyTorch 预训练的 ResNet18 模型
            pretrained_model = models.resnet18(pretrained=True)
            self.conv1 = pretrained_model.conv1
            self.bn1 = pretrained_model.bn1
=======
﻿import torch
import torch.nn as nn
import torch.nn.functional as F

from torchsummary import summary
import torchvision.models as models

class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super(ResidualBlock, self).__init__()
        #1- 第一个卷积层
        self.conv1 = nn.Conv2d(
            in_channels,
            out_channels,
            kernel_size=3,
            stride=stride,
            padding = 1,
            bias=False
        )
        self.bn1 = nn.BatchNorm2d(out_channels)
        #2- 第二个卷积层
        self.conv2 = nn.Conv2d(
            out_channels,
            out_channels,
            kernel_size=3,
            stride=1,
            padding=1,
            bias=False
        )
        self.bn2 = nn.BatchNorm2d(out_channels)
        #3- shortcut短接模块
        self.shortcut = nn.Sequential()
        # 如果短接模块输出与卷积模块输出不一致，则需要调整
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )
    #前向传播
    def forward(self,x):
        out = self.conv1(x)
        out = self.bn1(out)
        out = F.relu(out)
        
        out = self.conv2(out)
        out = self.bn2(out)
        #加上shortcut
        out += self.shortcut(x)
        out = F.relu(out)
        return out
    
class ResNet18(nn.Module):
    def __init__(self,pretrained=False,num_classes=10):
        super(ResNet18, self).__init__()
        if pretrained:
            pretrained_model=models.resnet18(pretrained=True)
            self.conv1 = pretrained_model.conv1
            self.bn1 = pretrained_model.bn1
            self.relu = pretrained_model.relu
            self.maxpool = pretrained_model.maxpool
>>>>>>> 68289aa8a069e37b565ef2714e3fc104c02b0be4
            self.layer1 = pretrained_model.layer1
            self.layer2 = pretrained_model.layer2
            self.layer3 = pretrained_model.layer3
            self.layer4 = pretrained_model.layer4
<<<<<<< HEAD
        '''


        # 1-构造初始的卷积层
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
            nn.BatchNorm2d(64)
        )
        self.in_channels = 64  # 更新输入通道数为当前层的输出通道数
        # 2-构造残差块ResidualBlock,由4个带有残差连接的块组成
        self.layer1 = self.make_layer(ResidualBlock, 64, 2, stride=1)   #2表示两个残差块，stride=1表示不改变特征图尺寸
        self.layer2 = self.make_layer(ResidualBlock, 128, 2, stride=2)
        self.layer3 = self.make_layer(ResidualBlock, 256, 2, stride=2)
        self.layer4 = self.make_layer(ResidualBlock, 512, 2, stride=2)
        # 3-构造全局平均池化层
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        # 4-构造全连接层
        self.fc = nn.Linear(512, num_classes)

    def make_layer(self, block, out_channels, blocks, stride=1):
        """
        构造多个残差块的层 (layer)。

        参数：
        - block: 残差块类型，例如 ResidualBlock。
        - out_channels: 每个残差块的输出通道数。
        - blocks: 残差块的数量。
        - stride: 第一个残差块的步幅，用于调整输入尺寸。

        返回：
        - nn.Sequential: 包含多个残差块的层。
        """
        layers = []
        # 第一个残差块，可能需要调整通道数或步幅
        layers.append(block(self.in_channels, out_channels, stride))
        # 更新输入通道数为当前层的输出通道数
        self.in_channels = out_channels
        # 后续的残差块，步幅固定为 1
        for _ in range(1, blocks):
            layers.append(block(out_channels, out_channels))
        return nn.Sequential(*layers)

    def forward(self, x):
        # 1-输入通过初始卷积层
        x = self.conv1(x)
        x = F.relu(x)
        # 2-输入通过残差块
=======
            self.avgpool = pretrained_model.avgpool
            self.fc = nn.Linear(pretrained_model.fc.in_features, num_classes)
        else :
            self.conv1 = nn.Sequential(
                nn.conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
                nn.BatchNorm2d(64)
            )
                
            
            self.in_channels = 64
            self.layer1 = self._make_layer(ResidualBlock, 64, 2, stride=1)
            self.layer2 = self._make_layer(ResidualBlock, 128, 2, stride=2)
            self.layer3 = self._make_layer(ResidualBlock, 256, 2, stride=2)
            self.layer4 = self._make_layer(ResidualBlock, 512, 2, stride=2)
            self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
            self.fc = nn.Linear(512, num_classes)
    
    def _make_layer(self,block, out_channels, num_blocks, stride=1):
        layers = []
        layers.append(block(self.in_channels, out_channels, stride))
        self.in_channels = out_channels
        for _ in range(1, num_blocks):
            layers.append(block(out_channels, out_channels))
        return nn.Sequential(*layers)
    
    def forward(self,x):
        x = self.conv1(x)
        x = F.relu(x)

>>>>>>> 68289aa8a069e37b565ef2714e3fc104c02b0be4
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
<<<<<<< HEAD
        # 3-输入通过全局平均池化层
        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        # 4-输入通过全连接层
        x = self.fc(x)
        return x

        
       
def get_resnet34(pretrained=False, num_classes=10): #是否预训练，默认
 
    # model = models.resnet18(pretrained=pretrained)#都是减半5次
    model = models.resnet34(pretrained=pretrained) 
    # model = ResNet18()

    if num_classes != 10:  # If number of classes is different, modify the classifier
        model.fc = torch.nn.Linear(model.fc.in_features, num_classes)   # 修改全连接层以适应新的类别数
    return model


def get_resnet18(.num_classes=10): #是否预训练 我们没有写这个功能
    model = ResNet18()
    print('ResNet18')
    if num_classes != 10:  # If number of classes is different, modify the classifier
        model.fc = torch.nn.Linear(model.fc.in_features, num_classes)
    return model






if __name__ == '__main__':
    # 加载 ResNet-34 模型
    model = get_resnet34(pretrained=False, num_classes=10).to('cuda')  # 将模型移动到 GPU
    print(model)  # 打印模型结构

    # 打印所有支持的模型名称
    all_models = sorted(name for name in dir(models) if callable(getattr(models, name)))
    print("\nAll supported models in torchvision.models:")
    print(all_models)
    summary(model,(3,32,32))
=======

        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x


def get_resnet34(pretrained=False,num_classes=10):
    model = models.resnet34(pretrained=pretrained)
    if num_classes != 10:
        model.fc = nn.Linear(model.fc.in_features,num_classes)
    return model

def get_resnet18(pretrained=False,num_classes=10):
    model = models.resnet18(pretrained=pretrained)
    if num_classes != 10:
        model.fc = nn.Linear(model.fc.in_features,num_classes)
    return model

if __name__ == "__main__":
    model = get_resnet18().to('cuda')
    # print(model)
    # all_models = sorted(name for name in models.__dict__ if name.islower() and not name.startswith("__") and callable(models.__dict__[name]))
    # print(all_models)
    summary(model,(3,32,32))

>>>>>>> 68289aa8a069e37b565ef2714e3fc104c02b0be4
